﻿#region License

// Another free, open solution by folks who get it.
// Copyright © 2011 Business in Unison, Inc.
// 
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#endregion

using System;
using System.Collections.Generic;
using System.Linq;

using Castle.DynamicProxy;

using FluentAssertions;

using Patterns.Collections;
using Patterns.Interception;
using Patterns.Text;

using TechTalk.SpecFlow;

namespace Patterns.Specifications.Interception
{
	[Binding]
	public class InterceptionSteps
	{
		private const string _interceptor = "interceptor";
		private const string _interceptorlog = "interceptorLog";
		private const string _proxy = "proxy";

		public static ComplexTestObject CurrentProxy
		{
			get { return ScenarioContext.Current.GetValue(_proxy) as ComplexTestObject; }
			set { ScenarioContext.Current[_proxy] = value; }
		}

		public static IInterceptor CurrentInterceptor
		{
			get { return ScenarioContext.Current.GetValue(_interceptor) as IInterceptor; }
			set { ScenarioContext.Current[_interceptor] = value; }
		}

		public static ICollection<string> CurrentLog
		{
			get { return ScenarioContext.Current.GetValue(_interceptorlog) as ICollection<string>; }
			set { ScenarioContext.Current[_interceptorlog] = value; }
		}

		[Given(@"I have created a simple(( performance metric)|(( error)? logging)) interceptor( that only recognizes one method)?$")]
		public void CreateSimpleLogger(string interceptorType, string metric, string logging, string error, string oneMethod)
		{
			const string preferredMethod = "GetText";
			const string brokenMethod = "Throw";
			var normalMethods = new[] { "Method1", "Method2", "Method3", "GetText" };

			var interceptor = new LambdaInterceptor();
			DateTime? start = null, end = null;
			CurrentLog = new List<string>();
			bool handlesErrors = !string.IsNullOrEmpty(error);

			if (!string.IsNullOrEmpty(metric))
				interceptor.Before(invocation => start = DateTime.Now)
					.After(invocation => end = DateTime.Now)
					.Finally(invocation => CurrentLog.Add(string.Format("{0} Completed in {1}", invocation.Method.Name, (end.Value - start.Value))));
			else if (!string.IsNullOrEmpty(logging))
			{
				if (!string.IsNullOrEmpty(error))
					interceptor.OnError((invocation, errorMessage) =>
					{
						CurrentLog.Add(string.Format("Error during {0}: {1}", invocation.Method.Name, errorMessage));
						return handlesErrors;
					});

				interceptor.After(invocation => CurrentLog.Add(string.Format("{0} Completed at {1}", invocation.Method.Name, DateTime.Now)));
			}

			if (!string.IsNullOrEmpty(oneMethod))
				interceptor.RunCondition(invocation => invocation.Method.Name == (handlesErrors ? brokenMethod : preferredMethod));
			else
				interceptor.RunCondition(invocation =>
				{
					bool isNormal = normalMethods.Any(name => name == invocation.Method.Name);
					return handlesErrors ? !isNormal : isNormal;
				});

			CurrentInterceptor = interceptor;
		}

		[When(@"I create a proxy using the test object and the interceptor")]
		public void CreateProxy()
		{
			CurrentProxy = GeneralSteps.CurrentTestObject.CloneAndIntercept(CurrentInterceptor);
		}

		[When(@"I call all (non-)?exceptional methods on the proxy")]
		public void CallMethods(string non)
		{
			bool blowUp = string.IsNullOrEmpty(non);

			if (blowUp)
			{
				CurrentProxy.Throw<Exception>();
				CurrentProxy.Throw<ArgumentNullException>();
				CurrentProxy.Throw<InvalidOperationException>();
				return;
			}

			CurrentProxy.Method1();
			CurrentProxy.Method2();
			CurrentProxy.Method3();
			CurrentProxy.GetText(string.Empty);
		}

		[Then(@"the (metric collection|log) should have received( error)? (metrics|entries) for (one|each) method call")]
		public void CheckLogEntries(string collectionOrLog, string error, string metricsOrEntries, string oneOrEach)
		{
			if (!string.IsNullOrEmpty(error))
			{
				CurrentLog.Count.Should().Be(3);
				return;
			}

			bool checkSingle = oneOrEach.IsEquivalentTo("one");

			CurrentLog.Count.Should().Be(checkSingle ? 1 : 4);
		}
	}
}